﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Collections;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.IO;

namespace Server
{
    /// <summary>
    /// Класс сервера, работающего с помощью сокетов.
    /// </summary>
    class SocketServer
    {
        /// <summary>
        /// Порт
        /// </summary>
        private int _port;

        /// <summary>
        /// Сокет сервера
        /// </summary>
        private Socket _socketServer;

        /// <summary>
        /// Максимальное количество очереди соединений
        /// </summary>
        private int _countConnects;

        /// <summary>
        /// Задержка таймера в миллисекундах
        /// </summary>
        private uint _timeIntervat;

        /// <summary>
        /// Список водключенных сокетов.
        /// </summary>
        private List<Socket> _socketList = new List<Socket>();

        /// <summary>
        /// Xml настройки сервера
        /// </summary>
        private ParserXML _settings = new ParserXML();

        /// <summary>
        /// Класс с "магическими" значениями
        /// </summary>
        private ServerSettings _classSettings = new ServerSettings();

        /// <summary>
        /// Инициализация сервера
        /// </summary>
        public SocketServer()
        {
            ParserXML loadSettings = new ParserXML();

            if (!File.Exists(this._classSettings.DefaultFileName))
            {
                this._settings.CreateXMLSettings();
                this.LoadSettings();
            }
            else
            {
                this.LoadSettings();
            }

            this._socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        /// <summary>
        /// Загрузка настроек сервера.
        /// </summary>
        private void LoadSettings()
        {
            this._settings.LoadXML();
            this._port = System.Convert.ToInt32(this._settings.Port);
            this._countConnects = System.Convert.ToInt32(this._settings.CountConnects);
            this._timeIntervat = System.Convert.ToUInt32(this._settings.TimerInterval);
        }

        /// <summary>
        /// Запуск сервера.
        /// </summary>
        public void RunServer()
        {
            try
            {
                IPEndPoint pointListenInputsConnects = new IPEndPoint(IPAddress.Any, this._port);
                this._socketServer.Bind(pointListenInputsConnects);
                this._socketServer.Listen(this._countConnects);
                this.RunAccept();
            }
            catch (SocketException error)
            {
                Console.WriteLine("Error running server: {0}", error.Message);
            }
        }

        /// <summary>
        /// Начать прослушивание канала
        /// </summary>
        private void RunAccept()
        {
            Console.WriteLine("Сервер запущен. Использует {0} порт. Время обновления обновления {1} мс", this._port, this._timeIntervat);
            TimerCallback function = Accept;
            AutoResetEvent autoReset = new AutoResetEvent(false);
            Timer timer = new Timer(function, autoReset, 0, this._timeIntervat);
            autoReset.WaitOne(System.Threading.Timeout.Infinite, false);
        }

        /// <summary>
        /// Прослушивание канала.
        /// </summary>
        private void Accept(Object stateInfo)
        {
            try
            {
                Socket inputClient = this._socketServer.Accept();
                if (!this._socketList.Contains(inputClient))
                {
                    this._socketList.Add(inputClient);
                }
                Thread threadReceive = new Thread(delegate()
                {
                    this.ReceiveMessage(inputClient);
                });
                threadReceive.Start();
            }
            catch (SocketException error)
            {
                Console.WriteLine("Error accepting: {0}", error.Message);
            }
        
        }

        /// <summary>
        /// Получение и рассылка сообщений
        /// </summary>
        /// <param name="socket">Сокет</param>
        private void ReceiveMessage(Socket socket)
        {
            while (true)
            {
                try
                {
                    if (socket.Available == 0)
                    {
                        continue;
                    }

                    byte[] inputText = new byte[socket.Available];
                    if (inputText.Length == 0)
                    {
                        continue;
                    }
                    else
                    {
                        socket.Receive(inputText);

                        string convertCurrentMessageToString = Encoding.UTF8.GetString(inputText);
                        DataXmlMessage structXmlMessage = this.ParserXmlMessage(convertCurrentMessageToString);
                        string ipInputSocket = this.GetIp(socket.RemoteEndPoint.ToString());
                        Console.WriteLine("Пользователь {0} отправил сообщение:\n{1}\nВремя отправки: {2}\nЛогин отправителя: {3}", ipInputSocket,  structXmlMessage.Text, structXmlMessage.Date, structXmlMessage.Login);
                        Console.WriteLine();
                        for (int i = 0; i < this._socketList.Count; ++i)
                        {
                            if (socket != this._socketList[i] && this._socketList[i].Connected)
                            {
                                this._socketList[i].Send(inputText);
                                Console.WriteLine("Сообщение было отправленно: {0}", this.GetIp(this._socketList[i].RemoteEndPoint.ToString()));
                            }
                        }
                        Console.WriteLine();
                    }
                }
                catch
                {
                    if (this._socketList.Contains(socket) == true && socket.Connected == false)
                    {
                        this._socketList.Remove(socket);
                    }
                }
            }
        }

        /// <summary>
        /// Получение Ip адреса из строки
        /// </summary>
        /// <param name="stringWithIp">Строка с Ip адресом.</param>
        /// <returns>Функция возвращает Ip адрес.</returns>
        private string GetIp(string stringWithIp)
        {
            return stringWithIp.Split(':')[0];
        }

        /// <summary>
        /// Структура с данными Xml сообщения.
        /// </summary>
        /// <param name="xmlMessage">Сообщение Xml</param>
        /// <returns>Функция возвращает структуру с дынными Xml сообщения.</returns>
        private DataXmlMessage ParserXmlMessage(string xmlMessage)
        {
            XElement xml = XElement.Parse(xmlMessage);
            DataXmlMessage structMessage = new DataXmlMessage();
            structMessage.Date = xml.Element(this._classSettings.MessageDateElement).Value;
            structMessage.Login = xml.Element(this._classSettings.MessageLoginElement).Value;
            structMessage.Text = xml.Element(this._classSettings.MessageTextElement).Value;
            return structMessage;
        }
    }

    /// <summary>
    /// Структура XML сообщения
    /// </summary>
    struct DataXmlMessage
    {
        public string Date;
        public string Login;
        public string Text;
    }
}
